<html>
    <head>
        <title>审计日志</title>
    </head>
    <body>
        <script>
            // 审计日志

                    /*
                        维基百科： "审计跟踪（也称为审计日志）是与安全相关的的时间顺序记录、记录集和/记录的目的地和来源，这些记录提供了在任何时候影响特性 操作
                        程序或事件的活动序列的书面证据"


                        ABP 提供了一个"可扩展的审计日志系统"，它通过"约定"自动化审计日志记录，并提供"配置点"来控制审计日志的级别。

                        通常会为每个Web请求创建和保存“审计日志对象”。它包括：

                            1、请求和响应详细信息（如URL、Http方法、浏览器信息、HTTP状态代码…等）。

                            2、已执行的操作（控制器操作和应用程序服务方法调用及其参数）。

                            3、实体更改发生在Web请求中。

                            4、异常信息（如果在执行请求时出现错误）。

                            5、请求持续时间（衡量应用程序的性能）。

                         启动模板已经配置了审计日志系统，适用于大多数应用程序。
                    */

                    /*
                        一、支持的数据库提供程序

                                Entity Framework Core提供程序完全支持。

                                MongoDB提供程序不支持实体更改日志记录。其他功能按预期工作。
                                
                    */


                    /*
                        二、使用审计日志

                               useAuditing() 中间件添加到 asp.net core 请求管道中。 

                               如果你使用启动模板时创建了应用程序，则已经添加了此中间件。

                    */

                    /*
                        三、审计日志选项类  AbpAuditingOptions

                               AbpAuditingOptions 是用于配置审计日志系统的 "主要选项类"。

                               你可以在模块的ConfigureServices 方法中配置它：

                                    Configure<AbpAuditingOptions>(options =>
                                    {
                                        options.IsEnabled = false; //Disables the auditing system
                                    });

                            
                               这里是你可以配置的选项列表：

                                    1、IsEnabled（默认值：true）：启用或禁用审计系统的根开关。如果此值为false，则不使用其他选项。

                                    2、HideErrors（默认值：true）：如果在保存审计日志对象时发生任何错误，审计日志系统会隐藏并写入常规的日志。
                                                                  如果保存审计日志对你的系统至关重要，请将其设置为false以在隐藏错误的情况下引发异常。

                                    3、IsEnabledForAnonymousUsers（默认值：true）：如果你只想为经过身份验证的用户编写审核日志，请将其设置为false。
                                                                                如果你保存匿名用户的审核日志，你将看到这些用户的UserId值为null。

                                    4、AlwaysLogOnException（默认值：true）：如果设置为true，它总是在异常/错误情况下保存审计日志，而不检查其他选项（除了IsEnabled，它完全禁用审计日志）。

                                    5、IsEnabledForIntegrationService（默认值：false）：默认情况下，集成服务禁用审核日志记录。将此属性设置为true以启用它。

                                    6、IsEnabledForGetRequests（默认值：false）：HTTP GET请求通常不会在数据库中进行任何更改，并且审计日志系统不会为GET请求保存审计日志对象。
                                                                               将其设置为true以对GET请求启用它。

                                    7、DisableLogActionInfo（默认值：false）：如果设置为true，将不再记录AuditLogActionInfo

                                    8、ApplicationName：如果多个应用程序正在将审计日志保存到单个数据库中，请将此属性设置为你的应用程序名称，这样你就可以区分不同应用程序的日志。
                                                        如果不设置，它将从IApplicationInfoAccessor.ApplicationName值，默认为条目程序集名称。

                                    9、IgnoredTypes：审计日志记录要忽略的Type列表。如果这是实体类型，则不会保存对这种类型实体的更改。序列化操作参数时也使用此列表。

                                    10、EntityHistorySelectors：选择器列表，用于确定是否选择实体类型来保存实体更改。有关详细信息，请参阅下面的部分

                                    11、SaveEntityHistoryWhenNavigationChanges（默认值：true）：如果设置为true，当任何导航属性更改时，它会将实体更改保存到审计日志中。

                                    12、Contributors：AuditLogContributor实现的列表。贡献者是扩展审计日志系统的一种方式。请参阅下面的“审计日志贡献者”部分。

                                    13、AlwaysLogSelectors：用于保存匹配条件的审核日志的选择器列表。
                    */

                    /*
                        四、如何配置哪些实体更改需要记录？

                                    使用 实体历史选择器 或者 [Audited] 和 [DisabledAduting] 特性

                                    1、实体历史选择器 EntityHistorySelector
                                            
                                                保存所有实体的所有更改将需要大量数据库空间。
                                            因此，审计日志系统不会为实体保存任何更改，除非你明确配置它。

                                            如果要保存所有实体的所有更改，只需使用AddAllEntities()扩展方法。
                                            eg:
                                                Configure<AbpAuditingOptions>(options =>
                                                {
                                                    options.EntityHistorySelectors.AddAllEntities(); // 记录所有实体的更改

                                                });  

                                            options.EntityHistorySelectors 实际上是一个"类型谓词列表"。你可以编写一个 lambda 表达式来定义你的过滤器。

                                            下面的示例选择器执行与上面定义的 AddAllEntities() 扩展方法相同的操作：

                                                    Configure<AbpAuditingOptions>(options =>
                                                    {
                                                        options.EntityHistorySelectors.Add(
                                                            new NamedTypeSelector(
                                                                "MySelectorName",
                                                                type =>
                                                                {
                                                                    if (typeof(IEntity).IsAssignableFrom(type)) // IsAssignableFrom(type) 可以被当前实体"赋值"的 IEntity类型
                                                                    {
                                                                        return true;
                                                                    }
                                                                    else
                                                                    {
                                                                        return false;
                                                                    }
                                                                }
                                                            )
                                                        );
                                                    });  

                                            条件typeof(IEntity).IsAssignableFrom(type)将为true（从技术上讲，这是应用程序中的所有实体）。你可以根据自己的偏好有条件地检查并返回true或false。
                                            
                                            options.EntityHistorySelectors 是一种灵活切动态的方式来选择设计日志记录的实体。
                                
                                    2、[Audited] 特性 和 [DisableAduting] 特性
                                                        
                                            另一种选择是否记录某个实体的方法是使用 [Audited] 和 [DisabledAuditing] 特性！！ (也是最简单的方法，不需要任何配置)       

                                                    
                    */

                    /*
                        五、AbpAspNetCoreAuditingUrlOptions 选项类

                                AbpAspNetCoreAuditingUrlOptions是选项对象，用于在ASP.NET核心层中配置审计日志记录。

                                你可以在模块的ConfigureServices方法中配置它：
                                                    
                                        Configure<AbpAspNetCoreAuditingUrlOptions>(options =>
                                        {
                                            options.IncludeQuery = true;
                                        });    
                                        
                                在这里，你可以配置的选项列表：

                                        IncludeSchema（默认值：false）：如果设置为true，它将在URL中包含模式

                                        IncludeHost（默认值：false）：如果设置为true，它将在URL中包含主机。

                                        IncludeQuery（默认值：false）：如果设置为true，它将在URL中包含查询字符串。
                    */

                    

                    /*
                        七、禁用、启用服务的审计日志

                                1、禁用/启用控制器(Controller)和操作(Action)中的审计日志

                                       默认情况下会记录所有控制器操作（有关GET请求，请参见上面的IsEnabledForGetRequests）。   
                                       
                                       你可以使用[DisableAuditing]为特定控制器(Controller)类型禁用它：

                                            [DisableAuditing] // 禁用Controller的审计日志
                                            public class HomeController : AbpController
                                            {
                                                //...
                                            }

                                       对任何操作(Action)使用[DisableAuditing]在操作级别对其进行控制：
                                            public class HomeController : AbpController
                                            {
                                                [DisableAuditing]  // 禁用Action的审计日志
                                                public async Task<ActionResult> Home()
                                                {
                                                    //...
                                                }

                                                public async Task<ActionResult> OtherActionLogged()
                                                {
                                                    //...
                                                }
                                            }

                                2、禁用/启用 应用程序服务(Application Services) 和 方法(Method)中的审计日志

                                        应用程序服务方法的调用也默认包含在审计日志中。
                                        你可以在服务或方法级别使用 [DisableAuditing] 特性来禁用它：

                                3、禁用/启用 其他类的审计日志
                                        
                                        可以为任何类型的类启用操作设计日志记录 (注入到依赖并从其解析)，而默认情况下仅为控制器和应用程序服务启用。

                                        可以对任何需要审计日志的类或方法使用 [Aduited] 和 [DisableAuditing] 特性。
                                    
                                        此外，你的类可以(直接或固有的) 实现 IAuditingEnabled接口一默认启用该类的设计日志记录。

                    */

                    /*
                        八、禁用/启用实体的统计日志

                                   在一下情况下，在实体变更实际日志记录中忽略实体：

                                                ①、如果将实体类型添加到 AbpAuditingOptions.IgnoredTypes  它将被设计日志系统完全忽略。

                                                ②、如果对象不是实体 (不是直接或固有地实现 IEntity接口 -----所有实体默认实现此接口)

                                                ③、如果实体类型不公开

                                        否则，你可以使用 [Audited] 特性，为实体启用实体更改设计日志记录：
                                                [Audited]
                                                public class MyEntity : Entity<Guid>
                                                {
                                                    //...
                                                }  
                                                    
                                        或者禁用它的更改设计日志：
                                                [DisableAuditing]
                                                public class MyEntity : Entity<Guid>
                                                {
                                                    //...
                                                }

                                        只有当实体被 AbpAuditingOptions.EntityHistorySelectors选中时，才需要禁用审计日志记录。

                                        你可以进仅禁用审核实体的某些属性，以详细控制审计日志记录：

                                                [Audited]
                                                public class MyUser : Entity<Guid>
                                                {
                                                    public string Name { get; set; }
                                                        
                                                    public string Email { get; set; }

                                                    [DisableAuditing] // 针对属性禁用审计日志
                                                    public string Password { get; set; }
                                                }
                                        核日志系统将保存MyUser实体的更改，同时忽略Password属性，出于安全目的保存可能很危险。

                                        在某些情况下，你可能希望保存一些属性，单忽略所有其他属性。
                                        为所有其他属性编写 [DisableAuditing] 会很乏味。在这种情况下，仅对所需属性使用 [Audited] 并使用 [DisableAuditing] 属性标记实体：

                                                [DisableAuditing]
                                                public class MyUser : Entity<Guid>
                                                {
                                                    [Audited] //Only log the Name change
                                                    public string Name { get; set; }

                                                    public string Email { get; set; }

                                                    public string Password { get; set; }
                                                }
                                                    
                    */

                    /*
                        九、IAuditingStore  用于保存审计日志对象的接口

                                IAuditingStore 是用于ABP保存审计日志对象的接口。 如果你需要将设计日志对象保存到"自定义数据存储中"，

                                你可以在自己的应用程序中实现IAuditingStore，并使用依赖注入系统进行替换。

                                SimpleLogAuditingStore  在没有审计日志存储时使用。它只是将审计对象写入 "标准日志记录系统"。

                                审计日志模块已经在启动模板中配置，将审计日志对象保存到数据库。所以，大多数时候你不关心 IAuditingStore如何实现和使用的。
                    */

                    /*
                        十、审计日志对象  AuditLogObject

                                默认情况下，为每个Web请求创建一个"审计日志对象"。

                                审计日志对象可以由以下关系图表示：
                                                    
                                        AuditLogInfo     包含：

                                                 AuditLogActionInfo     EntityChageInfo        Exception  Comment  

                                                                         包含：
                                                                         EntityChageInfo

                                1、AuditLogInfo 具有以下属性的根对象：
                                        
                                        1、ApplicationName： 当你将不同应用程序的审计日志保存到同一个数据库时，此属性用于区分应用程序的日志。

                                        2、UserId ：当前用户的ID，如果用户已登录。

                                        3、UserName：  当前用户的用户名，如果用户已登录（此值在这里不依赖于身份模块/系统进行查找）。

                                        4、TenantId：  当前租户的ID，用于多租户应用程序。

                                        5、TenantName： 当前租户的名称，用于多租户应用程序。

                                        6、ExecutionTime：创建此审计日志对象的时间。

                                        7、ExecutionDuration： 请求的总执行持续时间，以毫秒为单位。这可用于观察应用程序的性能

                                        8、ClientId： 当前客户端的ID，如果客户端已通过身份验证。客户端通常是通过HTTP API使用系统的第3方应用程序。

                                        9、ClientName：当前客户端的名称（如果可用）。

                                        10、ClientIpAddress：客户端/用户设备的IP地址。

                                        11、CorrelationId： 当前相关性ID。相关ID用于在单个逻辑操作中关联不同应用程序（或微服务）写入的审计日志。

                                        12、BrowserInfo：当前用户的浏览器名称/版本信息（如果可用）。

                                        13、HttpMethod： 当前请求的HTTP方法（GET、POST、PUT、DELETE…等）。

                                        14、HttpStatusCode：此请求的HTTP响应状态代码。

                                        15、Url:请求的URL。

                                2、AuditLogActionInfo:  
                                        审计日志操作通常是Web请求期间的控制器操作（Action） 或 应用程序服务方法调用。

                                        一个审计日志可能包含多个操作.

                                        操作对象具有以下属性：

                                                    1、ServiceName： 执行的控制器/服务的名称。

                                                    2、MethodName： 控制器/服务的执行方法的名称。

                                                    3、Parameters： JSON格式的文本，表示传递给方法的参数。

                                                    4、ExecutionTime：执行测方法的时间。

                                                    5、ExecutionDuration：  方法执行的持续时间，以毫秒为单位。这可以用来观察方法的性能。

                                3、EntityChangeInfo：
                                         表示此Web请求中实体的更改。审计日志可能包含零个或多个实体更改。
                                         实体更改具有以下属性：

                                                    1、ChangeTime：更改实体的时间。

                                                    2、ChangeType： 具有以下字段的枚举：Created（0）、Updated（1）和Deleted（2）。

                                                    3、EntityId： 已更改的实体ID

                                                    4、EntityTenantId： 该实体所属租户的ID

                                                    5、EntityTypeFullName：  键入具有完整命名空间的实体的（类）名称（如Book实体的Acme.BookStore.Book）。

                                4、EntityPropertyChangeInfo：
                                        表示实体属性的更改。
                                        实体更改信息可能包含具有以下属性的一个或者多个属性更改：

                                                    1、NewValue：属性的新值。如果实体被删除，则为null。

                                                    2、OriginalValue：更改前的旧/原始值。如果实体是新创建的，则为null。

                                                    3、PropertyName：实体类上的属性名称。

                                                    PropertyTypeFullName：键入具有完整命名空间的属性的（类）名称。

                                5、Exception：
                                          异常：审计日志对象可能包含零个或多个异常。通过这种方式，你可以获得失败请求的报告。
                                          
                                6、Comment：将自定义消息添加到审计日志条目的任意字符串值。审计日志对象可能包含零个或多个注释。
                                          
                                  
                                
                                除了上面解释的标准属性之外，AuditLogInfo、AuditLogActionInfo和EntityChangeInfo对象实现了IHasExtraProperties接口，因此你可以向这些对象添加自定义属性。
                    */

                    /*
                        十一、审计日志贡献者   Audit Log Contributors

                                你可以通过创建一个派生自 AuditLogContributor 类的类来扩展审计系统，该类定义了 PreContribute 和 PostContribute方法。

                                唯一预构建的贡献者是 AspNetCoreAuditLogContributor 类，它为Http请求设置相关属性。

                                贡献者可以设置 AuditLogInfo 类的属性和集合一添加更多信息：

                                eg：
                                    public class MyAuditLogContributor : AuditLogContributor // 继承此类创建审计日志贡献者
                                    {
                                        // 贡献前
                                        // 设置审计日志上下文的AuditInfo类的属性
                                        public override void PreContribute(AuditLogContributionContext context)
                                        {
                                            var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
                                            context.AuditInfo.SetProperty(  
                                                "MyCustomClaimValue",
                                                currentUser.FindClaimValue("MyCustomClaim") // 从用户请求中携带的某个声明中作为MyCustomClaimValue属性的值
                                            );
                                        }

                                        // 贡献后
                                        public override void PostContribute(AuditLogContributionContext context)
                                        {
                                            context.AuditInfo.Comments.Add("Some comment...");
                                        }
                                    }
                                    
                                    context.ServiceProvider可用于解析来自依赖注入的服务。

                                    context.AuditInfo可用于访问当前审计日志对象以对其进行操作。

                                创建这样的贡献者后，你必须将其添加到AbpAuditingOptions.Contributors列表中：

                                    Configure<AbpAuditingOptions>(options =>
                                    {
                                        options.Contributors.Add(new MyAuditLogContributor());
                                    });
                    */

                    /*
                        十二、IAuditLogScope 和 IAuditingManager   审计日志作用域  和 审计日志管理器
                                
                                本节介绍用于高级用例的 IAuditLogScope 和 IAuditingManger 服务。

                                “审计日志作用域”是一个环境范围，"构建"并"保存"审计日志对象。
                                默认情况下，审计日志中间件为Web请求创建"审计日志作用域"。


                                1、访问当前审计日志作用域

                                            审计日志贡献者，如上所述，是一种操作审计日志对象的全局方式。
                                            如果你能从服务中获得值，那就太好了。

                                            如果你需要在应用程序的任意点操作审计日对象，你可以访问"当前审计日志作用域"获取当前审计日志对象（与作用域的管理方式无关）

                                            eg:
                                                public class MyService : ITransientDependency
                                                {
                                                    private readonly IAuditingManager _auditingManager;

                                                    public MyService(IAuditingManager auditingManager)
                                                    {
                                                        _auditingManager = auditingManager; // 注入审计日志管理器
                                                    }

                                                    public async Task DoItAsync()
                                                    {
                                                        var currentAuditLogScope = _auditingManager.Current; // 从审计日志管理器获取当前审计日志作用域
                                                        if (currentAuditLogScope != null)
                                                        {
                                                            // 操作审计日志对象，添加备注
                                                            currentAuditLogScope.Log.Comments.Add(
                                                                "Executed the MyService.DoItAsync method :)"
                                                            );
                                                            
                                                            currentAuditLogScope.Log.SetProperty("MyCustomProperty", 42);
                                                        }
                                                    }
                                                }

                                            你应该始终检查 _auditiingManager.Current是否为空，因为它是在外部作用域控制的，
                                            并且在调用方法之前无法知道是否创建了审计作用域。


                                2、手动创建审计日志作用域

                                           你很少需要手动创建审计日志作用域，但是如果需要，你可以使用 IAuditingManager 创建审计日志作用域。
                                           
                                           eg:
                                                public class MyService : ITransientDependency
                                                {
                                                    private readonly IAuditingManager _auditingManager;

                                                    public MyService(IAuditingManager auditingManager)
                                                    {
                                                        _auditingManager = auditingManager; // 注入审计日志管理器
                                                    }

                                                    public async Task DoItAsync()
                                                    {
                                                        // 创建审计日志作用域
                                                        using (var auditingScope = _auditingManager.BeginScope())
                                                        {
                                                            try
                                                            {
                                                                // 调用其他服务
                                                            }
                                                            catch (Exception ex)
                                                            {
                                                                //操作审计日志对象，添加异常信息
                                                                _auditingManager.Current.Log.Exceptions.Add(ex);
                                                                throw;
                                                            }
                                                            finally
                                                            {
                                                                // 一定记得要保存日志
                                                                await auditingScope.SaveAsync();
                                                            }
                                                        }
                                                    }
                                                }

                                        你可以调用其他服务，它们可以调用其他服务，它们可以更改实体等等。所有这些交互都保存为最终块中的单个审计日志对象。
                    */

                    /*
                        十三、审计日志模块
                                
                                审计日志模块基本上实现了 IAuditingScope 将审计日志对象保存到数据库中。
                                它支持多个数据库提供程序。

                                该模块默认添加到启动模板中。

                                有关详细信息，请参阅审核日志模块文档。
                    */
        </script>
    </body>
</html>